/**
 * edurender
 * ersw_context.c
 * (c) 2009 J. Brad Byrd
 */

#include "ersw_context.h"

#include "../er/er_memory.h"
#include "../er/er_assert.h"
#include "../er/er_math.h"
#include "../er/er_hashtable.h"

#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

static ERSWContext *s_firstContext = NULL;
static ERSWContext *s_lastContext = NULL;

static ERuint HandleHashFunc(const void *key, ERuint size)
{
	ER_UNUSED(size);

	return *((ERuint *)key);
}

ERSWContext *erswCreateContext(ERushort width, ERushort height, void *window)
{
	ERSWContext *context;
	ERint i;
	
	HWND hwnd = (HWND)window;
	
	ER_ASSERT(hwnd != INVALID_HANDLE_VALUE);

	context = (ERSWContext *)ER_MALLOC(sizeof(ERSWContext));
	ER_ASSERT(context);
	
	context->window = hwnd;
	context->nextHandle = 1;
	context->depthEnable = ER_FALSE;
	context->depthMask = ER_TRUE;
	context->frontFace = ER_FRONT_FACE_CCW;
	context->cullFace = ER_CULL_FACE_NONE;
	erIdentityMatrix(&context->modelview);
	erIdentityMatrix(&context->projection);
	context->viewport.x = 0;
	context->viewport.y = 0;
	context->viewport.width = width;
	context->viewport.height = height;
	context->texture = NULL;

	for (i = 0; i < ERSW_MAX_RESOURCE; i++)
	{
		context->resourceTables[i] = erCreateHashTable(HandleHashFunc, NULL);
		ER_ASSERT(context->resourceTables[i]);
	}

	context->framebuffer.colors = (ERBgrColor *)ER_MALLOC(sizeof(ERBgrColor) * width * height);
	ER_ASSERT(context->framebuffer.colors);	
	context->framebuffer.depths = (ERint *)ER_MALLOC(sizeof(ERint) * width * height);
	ER_ASSERT(context->framebuffer.depths);
	memset(context->framebuffer.depths, (ERint)(1.0f * 0x7FFFFFFF - 1.0f), sizeof(ERint) * width * height);
	context->framebuffer.width = width;
	context->framebuffer.height = height;
	context->framebuffer.stride = width;
	
	context->posArray = NULL;
	context->texCoordArray[0] = NULL;
	context->texCoordArray[1] = NULL;
	
	context->posStride = 0;
	context->texCoordStride[0] = 0;
	context->texCoordStride[1] = 0;
	
	if (s_firstContext == NULL)
		s_firstContext = context;
	else
		s_lastContext->next = context;
		
	s_lastContext = context;
	context->next = NULL;
	
	return context;
}

static void FreeContext(ERSWContext *context)
{
	ERint i;
	
	for (i = 0; i < ERSW_MAX_RESOURCE; i++)
		erDestroyHashTable(context->resourceTables[i]);
	
	ER_FREE(context->framebuffer.colors);
	ER_FREE(context->framebuffer.depths);
	ER_FREE(context);
}

void erswDestroyContext(ERSWContext *context)
{
	ERSWContext *prev;
	ERSWContext *curr;
	
	ER_ASSERT(context);
	
	prev = NULL;
	curr = s_firstContext;
	
	while (curr != NULL)
	{
		if (curr == context)
		{
			if (prev == NULL)
				s_firstContext = curr->next;
			else			
				prev->next = curr->next;
			
			if (curr->next == NULL)
				s_lastContext->next = prev;
			
			break;
		}
		
		prev = curr;
		curr = curr->next;
	}
	
	FreeContext(context);
}

void erswDestroyAllContexts()
{
	ERSWContext *c = s_firstContext;
	
	while (c != NULL)
	{
		ERSWContext *next = c->next;
		FreeContext(c);
		c = next;
	}
	
	s_firstContext = 0;
	s_lastContext = 0;
}

ERHandle erswContextAddResource(ERSWContext *context, ERSWResource type, void *data)
{
	ERHandle handle = ER_INVALID_HANDLE;

	ER_ASSERT(context);
		
	if (!erHashTableInsert(context->resourceTables[type], &handle, sizeof(ERHandle), data))
		return handle;
	
	handle = context->nextHandle;
	context->nextHandle++;
	
	return handle;
}

void *erswContextFindResource(ERSWContext *context, ERSWResource type, ERHandle handle)
{
	void *resource = NULL;

	ER_ASSERT(context);
	
	erHashTableSearch(context->resourceTables[type], &handle, sizeof(ERHandle), &resource);
	
	return resource;
}

void erswContextRemoveResource(ERSWContext *context, ERSWResource type, ERHandle handle)
{
	ER_ASSERT(context);

	erHashTableRemove(context->resourceTables[type], &handle, sizeof(ERHandle));
}

ERResult erswSwapBuffers(ERSWContext *context)
{
	HDC hdc;
	BITMAPINFOHEADER bmih;
	
	hdc = GetDC((HWND)context->window);
			
	bmih.biSize = sizeof(BITMAPINFOHEADER);
	bmih.biWidth = context->framebuffer.width;
	bmih.biHeight = context->framebuffer.height;
	bmih.biPlanes = 1;
	bmih.biBitCount = 24;
	bmih.biCompression = BI_RGB;
	bmih.biSizeImage = 0;
	bmih.biXPelsPerMeter = 0;
	bmih.biYPelsPerMeter = 0;
	bmih.biClrUsed = 0;
	bmih.biClrImportant = 0;

	StretchDIBits(
		hdc,
		0, 0,
		context->framebuffer.width, context->framebuffer.height,
		0, 0,
		context->framebuffer.width, context->framebuffer.height,
		context->framebuffer.colors,
		(BITMAPINFO *)&bmih,
		DIB_RGB_COLORS,
		SRCCOPY);

	ReleaseDC((HWND)context->window, hdc);

	return ER_RESULT_OK;
}
